package com.wkg.lotterysystem.service.impl;

import com.wkg.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.wkg.lotterysystem.common.exception.ServiceException;
import com.wkg.lotterysystem.common.utils.JacksonUtils;
import com.wkg.lotterysystem.common.utils.RedisUtil;
import com.wkg.lotterysystem.controller.param.CreateActivityParam;
import com.wkg.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.wkg.lotterysystem.controller.param.CreateUserByActivityParam;
import com.wkg.lotterysystem.controller.param.PageParam;
import com.wkg.lotterysystem.dao.dataobject.*;
import com.wkg.lotterysystem.dao.mapper.*;
import com.wkg.lotterysystem.service.ActivityService;
import com.wkg.lotterysystem.service.dto.ActivityDTO;
import com.wkg.lotterysystem.service.dto.ActivityDetailDTO;
import com.wkg.lotterysystem.service.dto.CreateActivityDTO;
import com.wkg.lotterysystem.service.dto.PageListDTO;
import com.wkg.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.wkg.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.wkg.lotterysystem.service.enums.ActivityStatusEnum;
import com.wkg.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;


@Service
public class ActivityServiceImpl implements ActivityService {
    private static final Logger log = LoggerFactory.getLogger(ActivityServiceImpl.class);

    /**
     * 活动缓存前缀
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    /**
     * 活动缓存过期时间
     */
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)//设计到多表
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //校验活动信息是否正确
        checkActivityInfo(param);

        //保存活动信息
        ActivityDo activityDo = new ActivityDo();
        activityDo.setActivityName(param.getActivityName());
        activityDo.setDescription(param.getDescription());
        activityDo.setStatus(ActivityStatusEnum.RUNING.name());
        activityMapper.insert(activityDo);


        //保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDo> activityPrizeDoList = prizeParams.stream()
                .map(prizeParam -> {
                    ActivityPrizeDo activityPrizeDo = new ActivityPrizeDo();
                    activityPrizeDo.setActivityId(activityDo.getId());  //这个是如果获取的我知道
                    activityPrizeDo.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDo.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDo.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDo.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDo;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDoList);


        //保存活动关联人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDo> activityUserDoList = userParams.stream()
                .map(userParam -> {
                    ActivityUserDo activityUserDo = new ActivityUserDo();
                    activityUserDo.setActivityId(activityDo.getId());
                    activityUserDo.setUserId(userParam.getUserId());
                    activityUserDo.setUserName(userParam.getUserName());
                    activityUserDo.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDo;
                }).collect(Collectors.toList());

        activityUserMapper.batchInsert(activityUserDoList);

        //整合完整的活动信息，存放Redis
        // activityId：  ActivityDetailDTO:活动+奖品+人员

        //先获取奖品基本属性列表
        //获取查询到的奖品Id
        List<Integer> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());

        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);

        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDo, activityUserDoList,
                prizeDOList, activityPrizeDoList);
        cacheActivity(detailDTO);
        //构造返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDo.getId());
        return createActivityDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> findActivtyList(PageParam param) {
        //获取总量
        int total = activityMapper.count();
        //获取当前页列表
        List<ActivityDo> activityDoList = activityMapper.selectActivityList(param.offset(), param.getPageSize());

        List<ActivityDTO> activityDTOList = activityDoList
                .stream()
                .map(activityDo -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDo.getId());
                    activityDTO.setActivityName(activityDo.getActivityName());
                    activityDTO.setDescription(activityDo.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDo.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total, activityDTOList);
    }

    @Override
    public ActivityDetailDTO getActivityDetail(Integer activityId) {
        if (null == activityId) {
            log.info("查询活动详细信息失败,activityId:{}", activityId);
            return null;
        }
        //查询 redies
        ActivityDetailDTO detailDTO = getActivityFromCache(activityId);
        if (null != detailDTO) {
            log.info("查询活动详细信息成功！detailDTO={}", detailDTO);
            return detailDTO;
        }

        //如果reides不存在，查表
        //活动表
        ActivityDo activityDo = activityMapper.selectById(activityId);

        //活动奖品表
        List<ActivityPrizeDo> activityPrizeDoList = activityPrizeMapper.selectByActivityId(activityId);


        //活动人员表
        List<ActivityUserDo> activityUserDoList = activityUserMapper.selectByActivityId(activityId);

        //奖品表
        List<Integer> prizeIds = activityPrizeDoList.stream()
                .map(ActivityPrizeDo::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDO = prizeMapper.batchSelectByIds(prizeIds);
        //整合活动详情信息，存放Redis
        detailDTO = convertToActivityDetailDTO(activityDo, activityUserDoList, prizeDO, activityPrizeDoList);
        cacheActivity(detailDTO);
        //返回
        return detailDTO;
    }

    @Override
    public void cacheActivity(Integer activityId) {
        if (null == activityId) {
            log.warn("要缓存的id为空");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //redis查表  查询表数据:活动表,关联奖品表,关联人员表,奖品信息表
        //活动奖品表
        ActivityDo activityDo = activityMapper.selectById(activityId);
        if (null == activityDo) {
            log.warn("要缓存的id有误");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }

        //活动奖品表
        List<ActivityPrizeDo> activityPrizeDoList = activityPrizeMapper.selectByActivityId(activityId);

        //活动人员表
        List<ActivityUserDo> activityUserDoList = activityUserMapper.selectByActivityId(activityId);

        //奖品表
        List<Integer> prizeIds = activityPrizeDoList.stream()
                .map(ActivityPrizeDo::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDO = prizeMapper.batchSelectByIds(prizeIds);
        //整合活动详情信息，存放Redis
        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDo, activityUserDoList,
                prizeDO, activityPrizeDoList);
        cacheActivity(detailDTO);
    }

    /**
     * 缓存完整的活动信息 ActivityDetailDTO
     *
     * @param detailDTO
     */
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        //key: ACTIVITY_12
        //value: ActivityDetailDTO(JSON)
        if (null == detailDTO || null == detailDTO.getActivityId()) {
            log.warn("要缓存的活动信息不存在");
            return;
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                    JacksonUtils.writeValueAsString(detailDTO), ACTIVITY_TIMEOUT);
        } catch (Exception e) {
            log.error("缓存活动异常  ActivityDetailDTO：{}", JacksonUtils.writeValueAsString(detailDTO), e);
        }
    }

    /**
     * 根据活动Id从缓存中获取活动详细信息
     *
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Integer activityId) {
        if (null == activityId) {
            log.warn("获取活动数据的activityId为空！");
            return null;
        }
        try {
            String str = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if (!StringUtils.hasText(str)) {
                log.info("获取缓存活动数据为空！key={}", ACTIVITY_PREFIX + activityId);
                return null;
            }
            return JacksonUtils.readValue(str, ActivityDetailDTO.class);
        } catch (Exception e) {
            log.error("从缓存中获取活动信息异常,activity={}", ACTIVITY_PREFIX + activityId, e);
            return null;
        }

    }

    /**
     * 根据DO整合完整的信息
     *
     * @param activityDo
     * @param activityUserDoList
     * @param prizeDOList
     * @param activityPrizeDoList
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDo activityDo,
                                                         List<ActivityUserDo> activityUserDoList,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDo> activityPrizeDoList) {
        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        detailDTO.setActivityId(activityDo.getId());
        detailDTO.setActivityName(activityDo.getActivityName());
        detailDTO.setDesc(activityDo.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.forName(activityDo.getStatus()));

        //apDO: {prizeId , amount , status},{prizeId , amount , status}
        //pDO: {prizeid, name ...},{prizeid, name ...},{prizeid, name ...}
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDoList
                .stream()
                .map(apDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPriceId(apDO.getPrizeId());
                    //在 prizeDOList 中，查找第一个其 id 与 apDO 的 prizeId 相等的 PrizeDO 对象，并将其封装在一个 Optional 对象中。
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            //只有当 prizeDO 的 id 与 apDO.getPrizeId() 相等时，这个 prizeDO 对象才会被保留在流中，否则会被丢弃。
                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    //如果PrizeDO为空，不执行当前方法，不为空才执行
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });

                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));

                    return prizeDTO;
                }).collect(Collectors.toList());
        detailDTO.setPrizeDTOList(prizeDTOList);


        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDoList.stream()
                .map(auDO -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(auDO.getUserId());
                    userDTO.setUserName(auDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());

        detailDTO.setUserDTOList(userDTOList);
        return detailDTO;
    }


    /**
     * 校验活动有效性
     *
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if (null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        //人员id在人员表中是否存在
        List<Integer> userIds = param.getActivityUserList().stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Integer> existUserIds = userMapper.selectExistByIds(userIds);
        if (CollectionUtils.isEmpty(existUserIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(userId -> {
            if (!existUserIds.contains(userId)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        //校验奖品id在奖品表中是否存在
        List<Integer> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());

        List<Integer> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);
        if (CollectionUtils.isEmpty(existPrizeIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(prizeId -> {
            if (!existPrizeIds.contains(prizeId)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //人员数量大于等于奖品数量
        int userAmount = param.getActivityUserList().size();
        int prizeAmount = param.getActivityPrizeList().stream()
                .mapToInt(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if (userAmount < prizeAmount) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }

        //校验活动奖品等级有效性
        param.getActivityPrizeList().forEach(prize -> {
            if (null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });

    }
}
